﻿using System;
using System.Text;
using System.Xml;
using Majestic12;
using MIL.Html;

namespace SDT.CrawlSystem.Servers.Filters
{
    /// <summary>
    /// Written by TungNX
    /// </summary>
    public partial class Filter : IFilter
    {
        #region Data Fields
        protected Tag tag = null;
        public Tag TheTag
        {
            get { return tag; }
        }

        protected Txt txt = null;
        public Txt TheTxt
        {
            get { return txt; }
        }

        protected Encoding enc = Encoding.UTF8;
        public Encoding Encode
        {
            get { return enc; }
        }
        #endregion

        #region Constructors
        private void LoadNodes(XmlNode n)
        {
            if (n.SelectSingleNode("Tag") != null)
                tag = new Tag(n.SelectSingleNode("Tag"));

            if (n.SelectSingleNode("Txt") != null)
                txt = new Txt(n.SelectSingleNode("Txt"));

            if (n.SelectSingleNode("DataSource") != null)
                dataSource = FromString(n.SelectSingleNode("DataSource").InnerText);
        }

        public Filter(XmlNode n)
        {
            if (n == null)
                throw new ArgumentNullException();
            LoadNodes(n);
        }

        public Filter(XmlNode n, Encoding enc)
        {
            if (n == null)
                throw new ArgumentNullException();
            this.enc = enc;
            LoadNodes(n);
        }
        #endregion

        private void SetHtmlParserProperties(HTMLparser p)
        {
            // This is optional, but if you want high performance then you may want to set chunk hash mode to FALSE. 
            // This would result in tag params being added to string arrays in HTMLchunk object called sParams and sValues, 
            // with number of actual params being in iParams.
            //
            // When TRUE (and its default) tag params will be added to hashtable HTMLchunk (object).oParams
            p.SetChunkHashMode(false);

            // If you set this to true then original parsed HTML for given chunk will be kept - this will reduce performance somewhat, 
            // but may be desireable in some cases where reconstruction of HTML may be necessary
            p.bKeepRawHTML = false;

            // if set to true (it is false by default), then entities will be decoded: this is essential if you want to get strings 
            // that contain final representation of the data in HTML, however you should be aware that if you want to use such strings 
            // into output HTML string then you will need to do Entity encoding or same string may fail later
            p.bDecodeEntities = true;

            // We have option to keep most entities as is - only replace stuff like &nbsp; this is called Mini Entities mode - 
            // it is handy when HTML will need to be re-created after it was parsed, though in this case really entities 
            // should not be parsed at all
            p.bDecodeMiniEntities = true;

            if (!p.bDecodeEntities && p.bDecodeMiniEntities)
                p.InitMiniEntities();

            // If set to true, then in case of Comments and SCRIPT tags the data set to oHTML will be extracted BETWEEN those tags, 
            // rather than include complete RAW HTML that includes tags too this only works if auto extraction is enabled
            p.bAutoExtractBetweenTagsOnly = true;

            // If true then comments will be extracted automatically
            p.bAutoKeepComments = false;

            // If true then scripts will be extracted automatically
            p.bAutoKeepScripts = false;

            // If this option is true then whitespace before start of tag will be compressed to single space character in string: " ", 
            // if false then full whitespace before tag will be returned (slower) you may only want to set it to false if you want 
            // exact whitespace between tags, otherwise it is just a waste of CPU cycles
            p.bCompressWhiteSpaceBeforeTag = false;

            // If true (default) then tags with attributes marked as CLOSED (/ at the end) will be automatically forced to be considered 
            // as open tags - this is no good for XML parsing, but I keep it for backwards compatibility for my stuff as it makes 
            // it easier to avoid checking for same tag which is both closed or open
            p.bAutoMarkClosedTagsWithParamsAsOpen = false;

            // Set encoding
            p.SetEncoding(Encode);
        }

        #region Do Filter methods
        protected virtual string DoHtmFilter(string htm, ref string InnerTxt)
        {
            #region Check input data
            if (TheTag == null || TheTag.Name == null || TheTag.Name.Length == 0)
                throw new NotSupportedException();
            if (htm == null || htm.Length == 0)
            {
                InnerTxt = "";
                return "";
            }
            #endregion

            // Buffer
            var b = new StringBuilder();
            var t = new StringBuilder();

            try
            {
                using (var p = new HTMLparser(htm, Encode))
                {
                    // Set parser's properties
                    SetHtmlParserProperties(p);

                    var nOpenTags = 0;
                    var accept = false;
                    HTMLchunk oChunk = null;


                    while ((oChunk = p.ParseNext()) != null)
                    {
                        p.SetRawHTML(oChunk);
                        switch (oChunk.oType)
                        {
                            #region Ignore script & comment elements
                            case HTMLchunkType.Script:
                            case HTMLchunkType.Comment:
                                break;
                            #endregion
                            case HTMLchunkType.OpenTag:
                                if (!accept)
                                {
                                    // Check tag's name
                                    accept = oChunk.sTag == TheTag.Name;

                                    #region Check StartWith conditions
                                    if (accept)
                                    {
                                        accept = false;
                                        foreach (var s in TheTag.StartWithList)
                                        {
                                            if (oChunk.oHTML.StartsWith(s))
                                            {
                                                accept = true;
                                                break;
                                            }
                                        }
                                    }
                                    #endregion

                                    #region Check id (if have)
                                    if (accept && TheTag.Id != null)
                                        accept = oChunk.oHTML.Contains(TheTag.Id);
                                    #endregion
                                }

                                if (accept)
                                {
                                    if (oChunk.sTag == TheTag.Name)
                                        nOpenTags++;
                                    b.Append(oChunk.oHTML);
                                }

                                break;
                            case HTMLchunkType.CloseTag:
                                if (accept)
                                {
                                    if (oChunk.oHTML.LastIndexOf(TheTag.Name) >= 0)
                                        nOpenTags--;
                                    b.Append(oChunk.oHTML);
                                    accept = nOpenTags > 0;
                                }
                                break;
                            case HTMLchunkType.Text:
                                if (accept)
                                {
                                    b.Append(oChunk.oHTML);
                                    if (t.Length > 0)
                                        t.Append(' ').Append(oChunk.oHTML.Trim());
                                    else
                                        t.Append(oChunk.oHTML.Trim());
                                }
                                break;
                        };
                    }
                };
            }
            catch (Exception ex)
            {
                var msg = ex.Message;
            }

            InnerTxt = t.ToString();
            return b.ToString();
        }
        /// <summary>
        /// Thuan trong text khong co' ma HTML
        /// </summary>
        /// <param name="txt"></param>
        /// <returns></returns>
        protected virtual string DoTxtFilter(string txt)
        {
            #region Check input data
            if (TheTxt == null || txt == null || txt.Length == 0)
                return "";
            #endregion

            #region Check start with & end with condition
            if (!string.IsNullOrEmpty(TheTxt.StartWith))
            {
                var s = txt.IndexOf(TheTxt.StartWith, TheTxt.StartAt);
                if (s >= TheTxt.StartAt)
                {
                    if (!string.IsNullOrEmpty(TheTxt.EndWith))
                    {
                        var e = txt.IndexOf(TheTxt.EndWith, s + TheTxt.StartWith.Length);
                        if (e > s)
                            return txt.Substring(s + TheTxt.StartWith.Length, e - s - TheTxt.StartWith.Length).Trim();
                    }
                    else
                    {
                        var ret = txt.Substring(s + TheTxt.StartWith.Length).Trim();
                        if (TheTxt.MaxLength > 0)
                            ret = ret.Substring(0, Math.Min(TheTxt.MaxLength, ret.Length));
                        return ret;
                    }
                }
            }
            else if (!string.IsNullOrEmpty(TheTxt.EndWith))
            {
                var e = txt.IndexOf(TheTxt.EndWith, TheTxt.StartAt);
                if (e >= TheTxt.StartAt)
                    return txt.Substring(TheTxt.StartAt, e);
            }
            #endregion

            return "";
        }
        /// <summary>
        /// Neu region = web thi xu ly trong htm
        /// Nguoc lai xu ly trong text
        /// </summary>
        /// <param name="txt"></param>
        /// <param name="htm"></param>
        /// <returns></returns>
        protected virtual string DoTxtFilter(string txt, string htm)
        {
            if (this.DataSource == Regions.WEB) txt = htm;

            #region Check input data
            if (TheTxt == null || txt == null || txt.Length == 0)
                return "";
            #endregion

            #region Check start with & end with condition
            if (!string.IsNullOrEmpty(TheTxt.StartWith))
            {
                var s = txt.IndexOf(TheTxt.StartWith, TheTxt.StartAt);
                if (s >= TheTxt.StartAt)
                {
                    if (!string.IsNullOrEmpty(TheTxt.EndWith))
                    {
                        var e = txt.IndexOf(TheTxt.EndWith, s + TheTxt.StartWith.Length);
                        if (e > s)
                        {
                            string test = txt.Substring(s + TheTxt.StartWith.Length, e - s - TheTxt.StartWith.Length).Trim();
                            return txt.Substring(s + TheTxt.StartWith.Length, e - s - TheTxt.StartWith.Length).Trim();
                        }
                    }
                    else
                    {
                        var ret = txt.Substring(s + TheTxt.StartWith.Length).Trim();
                        if (TheTxt.MaxLength > 0)
                            ret = ret.Substring(0, Math.Min(TheTxt.MaxLength, ret.Length));
                        return ret;
                    }
                }
            }
            else if (!string.IsNullOrEmpty(TheTxt.EndWith))
            {
                var e = txt.IndexOf(TheTxt.EndWith, TheTxt.StartAt);
                if (e >= TheTxt.StartAt)
                    return txt.Substring(TheTxt.StartAt, e);
            }
            #endregion

            return "";
        }
        /// <summary>
        /// Modifier: TungNX
        /// Modified Date: 03/09/2010
        /// Des:  if (TheTag != null)    
        /// Add Line: 269,270
        /// </summary>
        /// <param name="htm"></param>
        /// <param name="txt"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public virtual string DoFilter(string htm, string txt, ref string output)
        {
            if (TheTag != null)           
            {
                if (string.IsNullOrEmpty(TheTag.AttValue) && string.IsNullOrEmpty(TheTag.AttName))
                    return DoHtmFilter(htm, ref output);
            }

            // Modified Date: 03/09/2010
            txt = string.IsNullOrEmpty(txt) ? htm : txt;
           
            output = null;
            //Modified Date: 28/09/2010
            //xu ly Txt trong noi dung htm
            //truoc day su dung xu ly thuan text trong  vung HTM
            return DoTxtFilter(txt, htm);
        }
        /// <summary>
        /// Modifier: TungNX
        /// Modified Date: 03/09/2010
        /// Des:  if (TheTag != null)    
        /// Do filter content , use library MIL
        /// </summary>
        /// <param name="htm"></param>
        /// <param name="txt"></param>
        /// <param name="output"></param>
        /// <returns></returns>
        public virtual string DoFilter(string htm, string txt, ref string output, bool isContent)
        {
            if (TheTag != null)             
            {
                if (!string.IsNullOrEmpty(TheTag.AttValue) && !string.IsNullOrEmpty(TheTag.AttName))
                {
                    HtmlDocument mDocument = HtmlDocument.Create(htm);


                    HtmlNodeCollection nodes = mDocument.Nodes.FindByName(TheTag.Name).
                        FindByAttributeNameValue(TheTag.AttName, TheTag.AttValue);
                    if (nodes != null && nodes.Count > 0)
                    {
                        HtmlElement el = (HtmlElement)nodes[0];

                        return el.HTML;
                    }
                    return string.Empty;

                }
                else if (string.IsNullOrEmpty(TheTag.AttValue) && string.IsNullOrEmpty(TheTag.AttName))
                    return DoFilter(htm, txt, ref output);
            }

            output = null;
            // Modified Date: 03/09/2010
            //txt = string.IsNullOrEmpty(txt) ? htm : txt;
            
            txt = string.IsNullOrEmpty(txt) ? htm : txt;
            //Modified Date: 28/09/2010
            //xu ly Txt trong noi dung htm
            //truoc day su dung xu ly thuan text trong  vung HTM
            return DoTxtFilter(txt, htm);
        }

        protected virtual string DoDateFilter(string htm, string txt, ref string output)
        {
            if (TheTag != null)            
            {
                if (!string.IsNullOrEmpty(TheTag.AttValue) && !string.IsNullOrEmpty(TheTag.AttName))
                {
                    HtmlDocument mDocument = HtmlDocument.Create(htm);

                    HtmlNodeCollection nodes = mDocument.Nodes.FindByName(TheTag.Name).
                        FindByAttributeNameValue(TheTag.AttName, TheTag.AttValue);
                    if (nodes != null && nodes.Count > 0)
                    {
                        HtmlElement el = (HtmlElement)nodes[0];
                        return el.HTML;
                    }
                    return string.Empty;

                }
                else if (string.IsNullOrEmpty(TheTag.AttValue) && string.IsNullOrEmpty(TheTag.AttName))
                    return DoFilter(htm, txt, ref output);
            }

            output = null;
            // Modified Date: 03/09/2010
            txt = string.IsNullOrEmpty(txt) ? htm : txt;

            //Modified Date: 28/09/2010
            //xu ly Txt trong noi dung htm
            //truoc day su dung xu ly thuan text trong  vung HTM
            return DoTxtFilter(txt, htm);
        }
        #endregion
    }
}